<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="gBtGh" id="gBtGh"><span data-lake-id="uc240ac83" id="uc240ac83">典型回答</span></h1>
  <p data-lake-id="u0f0bee9f" id="u0f0bee9f"><br></p>
  <p data-lake-id="uf3f88ce5" id="uf3f88ce5"><span data-lake-id="u75f27b0c" id="u75f27b0c">本地消息表其实也是借助消息来实现分布式事务的。</span></p>
  <p data-lake-id="u830289dd" id="u830289dd"><br></p>
  <p data-lake-id="u0f3513fe" id="u0f3513fe"><span data-lake-id="ua7d98859" id="ua7d98859">这个方案的</span><strong><span data-lake-id="ufccda486" id="ufccda486">主要思想是将分布式事务拆分为本地事务和消息事务两个部分</span></strong><span data-lake-id="u9f0129bf" id="u9f0129bf">，本地事务在本地数据库中进行提交或回滚，而消息事务则将消息写入消息中间件中，以实现消息的可靠投递和顺序性。</span></p>
  <p data-lake-id="u7ae87ba2" id="u7ae87ba2"><span data-lake-id="u7d47d85f" id="u7d47d85f">​</span><br></p>
  <p data-lake-id="u4c534db5" id="u4c534db5"><span data-lake-id="u0e15918c" id="u0e15918c">一般来说的做法是，在发送消息之前，先创建一条本地消息，并且保证写本地业务数据的操作，和，写本地消息记录的操作在同一个事务中。这样就能确保只要业务操作成功，本地消息一定可以写成功。</span></p>
  <p data-lake-id="u1f7d03ed" id="u1f7d03ed"><span data-lake-id="u280828e4" id="u280828e4">​</span><br></p>
  <p data-lake-id="ud55120b7" id="ud55120b7"><img src="https://cdn.nlark.com/yuque/0/2023/png/5378072/1679213543727-21ced2f1-d04e-4c36-a0f1-fd0b2912e998.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_66%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u6ad4ea3d" id="u6ad4ea3d"><span data-lake-id="ub2be1b76" id="ub2be1b76">​</span><br></p>
  <p data-lake-id="u739c3848" id="u739c3848"><span data-lake-id="ue0717431" id="ue0717431">然后再基于本地消息，调用MQ发送远程消息。</span></p>
  <p data-lake-id="u1d55447b" id="u1d55447b"><span data-lake-id="u8fa960fb" id="u8fa960fb">​</span><br></p>
  <p data-lake-id="ucc692f2f" id="ucc692f2f"><span data-lake-id="u8bf86870" id="u8bf86870">消息发出去之后，等待消费者消费，在消费者端，接收到消息之后，做业务处理，处理成功后再修改本地消息表的状态。</span></p>
  <p data-lake-id="udcbc97a7" id="udcbc97a7"><br></p>
  <p data-lake-id="u237d21bd" id="u237d21bd"><span data-lake-id="ucb8837f7" id="ucb8837f7">这个过程中，可能有几个步骤都可能发生失败，那么如果失败了怎么办呢？</span></p>
  <p data-lake-id="u45d94aa4" id="u45d94aa4"><span data-lake-id="uac097779" id="uac097779">​</span><br></p>
  <p data-lake-id="uc12aceba" id="uc12aceba"><span data-lake-id="u96770261" id="u96770261">1、2如果失败，因为在同一个事务中，所以事务会回滚，3及以后的步骤都不会执行。数据是一致的。</span></p>
  <p data-lake-id="u62c9f9cc" id="u62c9f9cc"><span data-lake-id="ua273e36c" id="ua273e36c">​</span><br></p>
  <p data-lake-id="u02aca830" id="u02aca830"><span data-lake-id="u55729242" id="u55729242">3如果失败，那么就需要有一个定时任务，不断的扫描本地消息数据，对于未成功的消息进行重新投递。</span></p>
  <p data-lake-id="u4b682c4f" id="u4b682c4f"><span data-lake-id="u839622dc" id="u839622dc">​</span><br></p>
  <p data-lake-id="u08e58aff" id="u08e58aff"><span data-lake-id="ue1fad457" id="ue1fad457">4、5如果失败，则依靠消息的重投机制，不断地重试。</span></p>
  <p data-lake-id="uda2cb380" id="uda2cb380"><span data-lake-id="u9cb1ac99" id="u9cb1ac99">​</span><br></p>
  <p data-lake-id="ue600a2bd" id="ue600a2bd"><span data-lake-id="uf2519adf" id="uf2519adf">6、7如果失败，那么就相当于两个分布式系统中的业务数据已经一致了，但是本地消息表的状态还是错的。这种情况也可以借助定时任务继续重投消息，让下游幂等消费再重新更改消息状态，或者本系统也可以通过定时任务去查询下游系统的状态，如果已经成功了，则直接推进消息状态即可。</span></p>
  <p data-lake-id="u3cb2a034" id="u3cb2a034"><span data-lake-id="u7850cf72" id="u7850cf72">​</span><br></p>
  <p data-lake-id="uc37df2ca" id="uc37df2ca"><img src="https://cdn.nlark.com/yuque/0/2023/png/5378072/1679214074334-3c57700a-30a7-44b2-b1cd-239dc1509da2.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_65%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="ub0b1d298" id="ub0b1d298"><br></p>
  <h1 data-lake-id="iGzHd" id="iGzHd"><span data-lake-id="ub3e9df5b" id="ub3e9df5b">扩展知识</span></h1>
  <p data-lake-id="u054e59da" id="u054e59da"><br></p>
  <h2 data-lake-id="UYu6W" id="UYu6W"><span data-lake-id="u34f6181b" id="u34f6181b">优缺点</span></h2>
  <p data-lake-id="u23a2c033" id="u23a2c033"><br></p>
  <p data-lake-id="u2b4f1b6e" id="u2b4f1b6e"><span data-lake-id="u5602a562" id="u5602a562">优点：</span></p>
  <ol list="ub2ab0792">
   <li fid="ub84e5ad7" data-lake-id="u6c1edbbe" id="u6c1edbbe"><span data-lake-id="ubf924053" id="ubf924053">可靠性高：基于本地消息表实现分布式事务，可以将本地消息的持久化和本地业务逻辑操作，放到一个事务中执行进行原子性的提交，从而保证了消息的可靠性。</span></li>
   <li fid="ub84e5ad7" data-lake-id="u6a6d48b3" id="u6a6d48b3"><span data-lake-id="ud0ae2b13" id="ud0ae2b13">可扩展性好：基于本地消息表实现分布式事务，可以将消息的发送和本地事务的执行分开处理，从而提高了系统的可扩展性。</span></li>
   <li fid="ub84e5ad7" data-lake-id="ud3febd01" id="ud3febd01"><span data-lake-id="u309ba834" id="u309ba834">适用范围广：基于本地消息表实现分布式事务，可以适用于多种不同的业务场景，可以满足不同业务场景下的需求。</span></li>
  </ol>
  <p data-lake-id="u14fca5fa" id="u14fca5fa"><span data-lake-id="u27e14adc" id="u27e14adc">缺点：</span></p>
  <ol list="ud3441a30">
   <li fid="u15adb428" data-lake-id="ub8635e33" id="ub8635e33"><span data-lake-id="ud228b71b" id="ud228b71b">实现复杂度高：基于本地消息表实现分布式事务，需要设计复杂的事务协议和消息发送机制，并且需要进行相应的异常处理和补偿操作，因此实现复杂度较高。</span></li>
   <li fid="u15adb428" data-lake-id="u8035944e" id="u8035944e"><span data-lake-id="u07c38a0c" id="u07c38a0c">系统性能受限：基于本地消息表实现分布式事务，需要将消息写入本地消息表，并且需要定时扫描本地消息表进行消息发送，因此对系统性能有一定影响。</span></li>
   <li fid="u15adb428" data-lake-id="uc887327e" id="uc887327e" style="text-align: left"><span data-lake-id="ud8bd70b3" id="ud8bd70b3">会带来消息堆积扫表慢、集中式扫表会影响正常业务、定时扫表存在延迟问题等问题。在下文中介绍：</span></li>
  </ol>
  <p data-lake-id="u3d6170fa" id="u3d6170fa"><br></p>
  <p data-lake-id="ua9271254" id="ua9271254"><span data-lake-id="ua2f6ebb3" id="ua2f6ebb3">​</span><br></p>
  <h2 data-lake-id="QK6PY" id="QK6PY"><span data-lake-id="ub5fc0278" id="ub5fc0278">代码实践</span></h2>
  <p data-lake-id="u8da3d047" id="u8da3d047"><br></p>
 </body>
</html>